EDA: Caracterización y distribución de los datos#
Líbrerias y modulos necesarios#
import json
import warnings
import pandas as pd
import plotly.express as px
warnings.filterwarnings('ignore')
import plotly.graph_objects as go
from urllib.request import urlopen
from funciones import grafico_datos_faltantes
from funciones import grafico_barras_categoricas
Conjunto de datos#
url = r"https://github.com/sePerezAlbor/Data/blob/main/data_analisis.xlsx?raw=true"
data = pd.read_excel(url, na_values = [" "])
data.head()
| Nombre_Departamento | Nombre_Municipio | sitio_def | año_def | mes_def | sexo | estado_civil | grupo_edad | nivel_edu | grupo_etnico | ... | seg_social | ent_salud_cod | prob_muerte | cert_medica | asistencia_med | causa_directa | causa_ant_1 | causa_basica | causa_OPS | region | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ANTIOQUIA | MEDELLÍN | Hospital o Clínica | 2014 | Julio | Femenino | Soltero | 15-19 años | Secundaria | Ninguno | ... | Subsidiado | EPS - Subsidiado | Natural | Historia clínica | SI | J969 | C782 | C509 | Tumor Maligno | Andina |
| 1 | ANTIOQUIA | APARTADÓ | Hospital o Clínica | 1997 | Agosto | Femenino | NaN | 15-19 años | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | C509 | NaN | Andina |
| 2 | ANTIOQUIA | ENVIGADO | Casa | 1986 | Abril | Femenino | Soltero | 15-19 años | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1749 | NaN | Andina |
| 3 | ATLÁNTICO | BARRANQUILLA | Hospital o Clínica | 1989 | Julio | Femenino | Casado | 15-19 años | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1749 | NaN | Atlántica |
| 4 | ATLÁNTICO | BARRANQUILLA | Hospital o Clínica | 1998 | Agosto | Femenino | Casado | 15-19 años | Secundaria | NaN | ... | NaN | NaN | Natural | Historia clínica | SI | J969 | C780 | C509 | Tumor Maligno | Atlántica |
5 rows × 21 columns
data.shape
(46869, 21)
Análisis general del conjunto de datos#
# Dividir en dos grupos (ajusta según necesidad)
categorical_columns = data.select_dtypes(include='object').columns
grupo1 = categorical_columns[:11] # Primeras 12 columnas
grupo2 = categorical_columns[11:] # Resto de columnas
display(data[grupo1].describe())
display(data[grupo2].describe())
| Nombre_Departamento | Nombre_Municipio | sitio_def | mes_def | sexo | estado_civil | grupo_edad | nivel_edu | grupo_etnico | depto_res | seg_social | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 46869 | 46867 | 46208 | 46869 | 46869 | 44256 | 46869 | 29214 | 15590 | 46869 | 33403 |
| unique | 33 | 872 | 3 | 12 | 1 | 4 | 14 | 5 | 6 | 33 | 6 |
| top | BOGOTA DC | BOGOTA DC | Casa | Mayo | Femenino | Casado | 50-54 años | Primaria | Ninguno | BOGOTA DC | Contributivo |
| freq | 10877 | 10877 | 24275 | 4095 | 46869 | 17585 | 8052 | 14445 | 14549 | 9686 | 17658 |
| ent_salud_cod | prob_muerte | cert_medica | asistencia_med | causa_directa | causa_ant_1 | causa_basica | causa_OPS | region | |
|---|---|---|---|---|---|---|---|---|---|
| count | 15685 | 33970 | 32963 | 33268 | 33877 | 29066 | 46869 | 33970 | 46869 |
| unique | 5 | 2 | 4 | 3 | 398 | 615 | 17 | 1 | 4 |
| top | EPS | Natural | Historia clínica | SI | C509 | C509 | C509 | Tumor Maligno | Andina |
| freq | 8497 | 33965 | 28059 | 30717 | 6843 | 8782 | 35166 | 33970 | 29448 |
Tratamiento de datos faltantes#
Note
En esta sección del proyecto (EDA), se analizarán los patrones de los valores faltantes previamente identificados en la fase de ETL. Se explorará su distribución a lo largo del dataset y su posible relación con otras variables, determinando si la ausencia de datos sigue algún patrón específico o es aleatoria. Además, se evaluarán diferentes estrategias de imputación, como el uso de la media, mediana, moda o modelos predictivos, según la naturaleza de cada variable. Este análisis permitirá minimizar el impacto de los datos faltantes y mejorar la calidad de las conclusiones obtenidas a partir del conjunto de datos.
Veamos primero, la cantidad de datos faltantes en general.
grafico_datos_faltantes(data)
Variable: Grupo étnico (grupo_etnico)#
display(data['grupo_etnico'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| grupo_etnico | |
| NaN | 31279 |
| Ninguno | 14549 |
| Afrodescendiente | 894 |
| Indígena | 87 |
| Gitano | 33 |
| Raizal | 21 |
| Palenquero | 6 |
data['grupo_etnico'] = data['grupo_etnico'].fillna('Ninguno')
Variable: Entidad de salud del paciente (ent_salud_cod)#
display(data['ent_salud_cod'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| ent_salud_cod | |
| NaN | 31184 |
| EPS | 8497 |
| EPS - Subsidiado | 6375 |
| EESS | 659 |
| ESE | 110 |
| EAPB | 44 |
data['ent_salud_cod'].fillna("No especificado", inplace=True)
Variable: Primera causa asociada a la defunción por antecendentes (causa_ant_1)#
display(data['causa_ant_1'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| causa_ant_1 | |
| NaN | 17803 |
| C509 | 8782 |
| C780 | 3904 |
| C80X | 1192 |
| C793 | 980 |
| ... | ... |
| K291 | 1 |
| I742 | 1 |
| I311 | 1 |
| J91 | 1 |
| J188 | 1 |
616 rows × 1 columns
moda = data['causa_ant_1'].mode()[0]
data['causa_ant_1'].fillna(moda, inplace=True)
Variable: Nivel de educación (nivel_edu)#
display(data['nivel_edu'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| nivel_edu | |
| NaN | 17655 |
| Primaria | 14445 |
| Secundaria | 8775 |
| Superior | 3417 |
| Ninguno | 2411 |
| Preescolar | 166 |
data['nivel_edu'].fillna("No especificado", inplace=True)
Variable: Tipo de certificación médica de la defunción (cert_medica)#
display(data['cert_medica'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| cert_medica | |
| Historia clínica | 28059 |
| NaN | 13906 |
| Interrogatorio a familiares o testigos | 4715 |
| Necropsia | 122 |
| Pruebas de laboratorio | 67 |
data['cert_medica'].fillna("No especificado", inplace=True)
Variable: Causa directa de la defunción (causa_directa)#
display(data['causa_directa'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| causa_directa | |
| NaN | 12992 |
| C509 | 6843 |
| I469 | 4155 |
| J969 | 4018 |
| J960 | 3481 |
| ... | ... |
| C969 | 1 |
| K469 | 1 |
| K570 | 1 |
| I808 | 1 |
| J952 | 1 |
399 rows × 1 columns
moda = data['causa_directa'].mode()[0]
data['causa_directa'].fillna(moda, inplace=True)
Variable: Asistencia médica durante la defunción (asistencia_med)#
display(data['asistencia_med'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| asistencia_med | |
| SI | 30717 |
| NaN | 13601 |
| NO | 2458 |
| Ignorado | 93 |
data['asistencia_med'].fillna("NO", inplace=True)
data['asistencia_med'].replace({"Ignorado": "NO"}, inplace=True)
Variable: Causa según clasificación OPS (causa_OPS)#
display(data['causa_OPS'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| causa_OPS | |
| Tumor Maligno | 33970 |
| NaN | 12899 |
data['causa_OPS'].fillna("Diferente a Tumor", inplace=True)
Variable: Tipo de causa muerte (prob_muerte)#
display(data['prob_muerte'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| prob_muerte | |
| Natural | 33965 |
| NaN | 12899 |
| En estudio | 5 |
data.drop('prob_muerte', axis=1, inplace=True)
Variable: Estado civil (estado_civil)#
display(data['estado_civil'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| estado_civil | |
| Casado | 17585 |
| Soltero | 10428 |
| Viudo | 9303 |
| Unión Libre, Divorciado/Otro | 6940 |
| NaN | 2613 |
data['estado_civil'].fillna("No especificado", inplace=True)
Variable: Sitio de defunción (sitio_def)#
display(data['sitio_def'].value_counts(dropna=False).to_frame())
| count | |
|---|---|
| sitio_def | |
| Casa | 24275 |
| Hospital o Clínica | 21425 |
| NaN | 661 |
| Otro Sitio | 508 |
moda = data['sitio_def'].mode()[0]
data['sitio_def'].fillna(moda, inplace=True)
Resultados de imputación#
grafico_datos_faltantes(data)
data.head()
| Nombre_Departamento | Nombre_Municipio | sitio_def | año_def | mes_def | sexo | estado_civil | grupo_edad | nivel_edu | grupo_etnico | depto_res | seg_social | ent_salud_cod | cert_medica | asistencia_med | causa_directa | causa_ant_1 | causa_basica | causa_OPS | region | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ANTIOQUIA | MEDELLÍN | Hospital o Clínica | 2014 | Julio | Femenino | Soltero | 15-19 años | Secundaria | Ninguno | ANTIOQUIA | Subsidiado | EPS - Subsidiado | Historia clínica | SI | J969 | C782 | C509 | Tumor Maligno | Andina |
| 1 | ANTIOQUIA | APARTADÓ | Hospital o Clínica | 1997 | Agosto | Femenino | No especificado | 15-19 años | No especificado | Ninguno | ANTIOQUIA | No especificado | No especificado | No especificado | NO | C509 | C509 | C509 | Diferente a Tumor | Andina |
| 2 | ANTIOQUIA | ENVIGADO | Casa | 1986 | Abril | Femenino | Soltero | 15-19 años | No especificado | Ninguno | ANTIOQUIA | No especificado | No especificado | No especificado | NO | C509 | C509 | 1749 | Diferente a Tumor | Andina |
| 3 | ATLÁNTICO | BARRANQUILLA | Hospital o Clínica | 1989 | Julio | Femenino | Casado | 15-19 años | No especificado | Ninguno | ATLÁNTICO | No especificado | No especificado | No especificado | NO | C509 | C509 | 1749 | Diferente a Tumor | Atlántica |
| 4 | ATLÁNTICO | BARRANQUILLA | Hospital o Clínica | 1998 | Agosto | Femenino | Casado | 15-19 años | Secundaria | Ninguno | ATLÁNTICO | No especificado | No especificado | Historia clínica | SI | J969 | C780 | C509 | Tumor Maligno | Atlántica |
data.to_excel(r"C:\Users\kamac\OneDrive\Desktop\SeminarioInvestigativoUN\data_clean.xlsx", index=False)
Análisis Univariado#
grafico_barras_categoricas(data, 'Nombre_Departamento', 'Departamento', 'Cantidad de defunciones', 'Defunciones por departamento')
grafico_barras_categoricas(data, 'mes_def', 'Mes de defunción', 'Cantidad de defunciones', 'Defunciones por mes')
grafico_barras_categoricas(data, 'sitio_def', 'Sitio de defunción', 'Cantidad de registros', 'Distribución sitio de defunción')
grafico_barras_categoricas(data, 'estado_civil', 'Estado civil', 'Cantidad de registros', 'Distribución estado civil')
grafico_barras_categoricas(data, 'grupo_edad', 'Grupo de edad', 'Cantidad de registros', 'Distribución grupo de edad')
grafico_barras_categoricas(data, 'nivel_edu', 'Nivel educativo', 'Cantidad de registros', 'Distribución nivel educativo')
grafico_barras_categoricas(data, 'grupo_etnico', 'Grupo étnico', 'Cantidad de registros', 'Distribución grupo étnico')
grafico_barras_categoricas(data,'seg_social', 'Seguridad social', 'Cantidad de registros', 'Distribución seguridad social')
grafico_barras_categoricas(data, 'ent_salud_cod', 'Entidad de salud', 'Cantidad de registros', 'Distribución entidad de salud')
grafico_barras_categoricas(data, 'cert_medica', 'Certificación médica', 'Cantidad de registros', 'Distribución certificación médica')
grafico_barras_categoricas(data, 'asistencia_med', 'Asistencia médica', 'Cantidad de registros', 'Distribución asistencia médica')
grafico_barras_categoricas(data, 'causa_basica', 'Causa básica', 'Cantidad de registros', 'Distribución causa básica')
grafico_barras_categoricas(data, 'causa_OPS', 'Causa OPS', 'Cantidad de registros', 'Distribución causa OPS')
grafico_barras_categoricas(data,'region', 'Región', 'Cantidad de registros', 'Distribución región')
Tabla de caracterización#

Análisis Bivariado#
Tendencia de defunciones por año y región#
data_grouped = data.groupby(['año_def', 'region']).size().reset_index(name='Defunciones')
color_palette = ["#ffb6c1", "#ff69b4", "#db7093", "#c71585",
"#ff1493", "#e60073", "#cc0033", "#b20000"]
fig6 = px.line(data_grouped,
x='año_def',
y='Defunciones',
color='region',
title='Tendencia de Defunciones por Año y Región',
markers=True, # Agregar marcadores
color_discrete_sequence=color_palette) # Aplicar paleta personalizada
fig6.update_layout(
font=dict(family="Bahnschrift", color="black"), # Fuente Bahnschrift en negro
xaxis_title="Año de Defunción",
yaxis_title="Número de Defunciones",
legend_title="Región", # Título de la leyenda
template="plotly_white" # Fondo blanco para mayor claridad
)
fig6.show()
Distribución de Defunciones por Grupo de Edad y Estado Civil#
color_palette = ["#ffb6c1", "#ff69b4", "#db7093", "#c71585",
"#ff1493", "#e60073", "#cc0033", "#b20000"]
fig = px.box(data,
x="estado_civil",
y="grupo_edad",
title="Distribución de Defunciones por Grupo de Edad y Estado Civil",
color="estado_civil",
color_discrete_sequence=color_palette,
labels={"estado_civil": "Estado Civil"})
fig.update_layout(
font=dict(family="Bahnschrift", color="black"), # Fuente en negro
xaxis_title="Estado Civil",
yaxis_title="Grupo de Edad",
width=1100, # Ajustar ancho
height=800 # Ajustar alto
)
fig.show()
Distribución de Edad de Defunciones por Año#
color_palette = ["#ffb6c1", "#ff69b4", "#db7093", "#c71585",
"#ff1493", "#e60073", "#cc0033", "#b20000",
"#990000", "#800000", "#ff3366", "#ff0033",
"#d40000", "#a10000"]
fig = px.box(data,
x="año_def",
y="grupo_edad",
title="Distribución de Edad de Defunciones por Año",
color="grupo_edad",
color_discrete_sequence=color_palette, labels={"grupo_edad": 'Grupo de edad'})
fig.update_layout(
font=dict(family="Bahnschrift", color="black"),
xaxis_title="Año de Defunción",
yaxis_title="Grupo de Edad",
width=1200, # Ancho del gráfico
height=1000 # Alto del gráfico
)
fig.show()
Evolución de las Defunciones según el Lugar de Muerte#
# Agrupar datos por año de defunción y sitio de defunción
data_grouped = data.groupby(["año_def", "sitio_def"]).size().reset_index(name="count")
# Gráfico de líneas con tonalidades rosadas
fig = px.line(data_grouped,
x="año_def",
y="count",
color="sitio_def",
markers=True, # Agregar puntos en la línea
title="Evolución de las Defunciones según el Lugar de Muerte",
color_discrete_sequence=["#ffb6c1", "#ff69b4", "#db7093", "#c71585"]) # Tonos rosados
fig.update_layout(
font=dict(family="Bahnschrift", color="black"),
xaxis_title="Año de Defunción",
yaxis_title="Número de Defunciones",
legend_title="Lugar de Defunción",
template="plotly_white"
)
fig.show()
Evolución de Defunciones por Grupo de Edad#
data_grouped = data.groupby(["año_def", "grupo_edad"]).size().reset_index(name="count")
color_palette = ["#ffb6c1", "#ff69b4", "#db7093", "#c71585",
"#ff1493", "#e60073", "#cc0033", "#b20000",
"#990000", "#800000", "#ff3366", "#ff0033",
"#d40000", "#a10000"]
fig = px.line(data_grouped,
x="año_def",
y="count",
color="grupo_edad",
title="Evolución de Defunciones por Grupo de Edad",
markers=True, # Agregar puntos en la línea
color_discrete_sequence=color_palette, # Aplicar paleta de colores
labels={"grupo_edad": "Grupo de Edad", "año_def": "Año", "count": "Número de Defunciones"})
fig.update_layout(
font=dict(family="Bahnschrift", color="black"), # Fuente en negro
xaxis_title="Año de Defunción",
yaxis_title="Número de Defunciones",
legend_title="Grupo de Edad",
width=1200,
height=800,
template="plotly_white"
)
fig.show()
Análisis de casos a lo largo del tiempo#
meses_map = {
"Enero": "01", "Febrero": "02", "Marzo": "03", "Abril": "04", "Mayo": "05", "Junio": "06",
"Julio": "07", "Agosto": "08", "Septiembre": "09", "Octubre": "10", "Noviembre": "11", "Diciembre": "12"
}
data['mes_def'] = data['mes_def'].map(meses_map)
data['Fecha'] = data['año_def'].astype(str) + '-' + data['mes_def']
conteo_fechas = data['Fecha'].value_counts().reset_index()
conteo_fechas.columns = ['Fecha', 'Cantidad']
conteo_fechas = conteo_fechas.sort_values('Fecha')
conteo_fechas['Fecha'] = pd.to_datetime(conteo_fechas['Fecha'], format='%Y-%m')
fig = px.line(conteo_fechas,
x='Fecha',
y='Cantidad',
title="Casos de Defunción a lo Largo del Tiempo",
labels={'Cantidad': 'Número de Defunciones', 'Fecha': 'Fecha'},
markers=True)
fig.update_traces(line=dict(color="#C2185B"),
marker=dict(color="#F06292", size=8))
fig.update_layout(
template="plotly_white",
xaxis_title="Fecha",
yaxis_title="Número de Defunciones",
font=dict(family="Bahnschrift", size=14, color="black"),
title_font=dict(family="Bahnschrift", size=22, color="black"),
plot_bgcolor="white",
paper_bgcolor="white"
)
fig.show()
Mapa de casos por departamento#
with urlopen('https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/Colombia.geo.json') as response:
counties = json.load(response)
locs=[]; z_id=[];
for loc in counties['features']:
loc['id'] = loc['properties']['NOMBRE_DPT']
locs.append(loc['properties']['NOMBRE_DPT'])
z_id.append(loc['properties']['HECTARES'])
colorscale = [
[0, "#fde0dc"], # Rosa claro
[0.2, "#f9bdbb"],
[0.4, "#f69988"],
[0.6, "#f36c60"],
[0.8, "#e84e40"],
[1, "#d50000"] # Rojo intenso
]
# Contar la cantidad de casos por departamento
casos_por_departamento = data['Nombre_Departamento'].value_counts().reset_index()
casos_por_departamento.columns = ['Nombre_Departamento', 'Casos']
# Reemplazar nombres para que coincidan con el GeoJSON
correccion_nombres = {
'ATLÁNTICO': 'ATLANTICO',
'BOLÍVAR': 'BOLIVAR',
'BOGOTA DC': 'SANTAFE DE BOGOTA D.C',
'BOYACÁ': 'BOYACA',
'CÓRDOBA': 'CORDOBA',
'CAQUETÁ': 'CAQUETA',
'CHOCÓ': 'CHOCO',
'LA GUAJIRA': 'LA GUAJIRA',
'ARCHIPIÉLAGO DE SAN ANDRÉS Y PROVIDENCIA Y SANTA CATALINA': 'ARCHIPIELAGO DE SAN ANDRES PROVIDENCIA Y SANTA CATALINA',
'GUAINÍA': 'GUAINIA',
'GUAVIARE': 'GUAVIARE',
'VAUPÉS': 'VAUPES',
'VICHADA': 'VICHADA'
}
casos_por_departamento['Nombre_Departamento'] = casos_por_departamento['Nombre_Departamento'].replace(correccion_nombres)
locs = casos_por_departamento['Nombre_Departamento'].tolist()
z_id = casos_por_departamento['Casos'].tolist()
fig = go.Figure(go.Choroplethmapbox(
geojson=counties,
locations=locs,
z=z_id,
colorscale= colorscale,
colorbar_title="Casos de Defunción"))
fig.update_layout(mapbox_style="open-street-map",
mapbox_zoom=4,
mapbox_center={"lat": 4.570868, "lon": -74.2973328} ,width=1200,
height=800 )
fig.show()
Pendientes#
Resolver dudas faltantes.
Incluir en los análisis las causas CIE-10
Interpretar
Correción de mapa para que aparezca San Andres